package com.instpower.things.service;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.instpower.things.config.Configs;
import com.instpower.things.config.PropertyUtil;
import com.instpower.things.entity.CabinetCacheVO;
import com.instpower.things.entity.RentOrder;
import com.instpower.things.entity.ResponseMQ;
import com.instpower.things.helper.Checksum;
import com.instpower.things.helper.DateUtil;
import com.instpower.things.instpower.Connection;
import com.instpower.things.instpower.Context;
import com.instpower.things.instpower.DeviceSession;
import com.instpower.things.instpower.DeviceSessionManager;
import com.instpower.things.transport.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.StringUtils;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName RentHandler
 * @Author: hellmash@gmail.com
 * @Date: 2020/3/20
 * @Version: 1.0.0
 * @Description: 消息处理
 */
public class RentHandler implements MessageListener {

    private final static Logger logger = LoggerFactory.getLogger("CORE");

    private static final ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private InstService instService;

    @Autowired
    private MessageService messageService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public static void main(String[] args) {

    }

    @Override
    public void onMessage(Message message) {
        try {
            JsonNode msg = objectMapper.readTree(message.getBody());
            logger.info("获取到租还指令msg{}", msg.toString());
            logger.info("获取到租还指令RENT:::{}", msg.get("cabinetSn").asText());
            String orderNo = msg.get("orderNo").asText();

            Response resp = new Response();
            Header header = new Header();
            String sn = msg.get("cabinetSn").asText();
            int requestType = msg.get("requestType").asInt();
            if (requestType == 0) {
                int orderId = msg.get("orderId").asInt();
                if (orderId > 0 && !instService.checkOrderStatus(orderId)) {
                    logger.info("rent,无法租借,结束{}", msg.toString());
                    return;
                }
            }
            header.setPtrol((byte) 0x02);
            header.setAddr(sn);
            header.setCommand((short) 0x08F5);
            DataBuffer data = new DataBuffer(6);
            data.writeInt((int) (System.currentTimeMillis()/1000));

            switch (requestType) {
                case 1:
                    data.writeByte(1);
                    break;
                case 2:
                    data.writeByte(2);
                    break;
                default:
                    data.writeByte(0);
                    break;
            }

            byte slotNum = Byte.parseByte(msg.get("slotNum").asText());
            data.writeByte(slotNum);
            MTData mDataR = new MTData();
            mDataR.setDatas(data.readBytes(6));
            header.setLength((short) (8));
            resp.setHeader(header);
            resp.setData(mDataR);

            DataBuffer buffer = resp.encode();
            int checksum = Checksum.crc16(Checksum.CRC16_ANKER,
                    ByteBuffer.wrap(buffer.readByteArray(buffer.readableBytes())));

            //do release after read
            buffer.release();

            Ender ender = new Ender();
            ender.setChecksum(checksum);
            resp.setEnder(ender);

            logger.debug("=======>租还报文组建完成!");
            String cacheOrderNoKey = String.format(Configs.ANKER_RENT_RETURN_ORDER_NO, (sn + slotNum));
            String cacheOrderNo = redisTemplate.opsForValue().get(cacheOrderNoKey);
            if (!StringUtils.isEmpty(cacheOrderNo)){
                logger.error("Cabinet exist [orderNO:{}] pending order cabinet sn:{},orderNo:{}",cacheOrderNo,sn,orderNo);
                messageService.send(sn,getBatterySn(sn,(int)slotNum),false,slotNum,orderNo,requestType,99,"");
                return;
            }
            redisTemplate.opsForValue().set(cacheOrderNoKey,orderNo,30,TimeUnit.SECONDS);


            String cacheKey = String.format(Configs.RENT_ORDER_KEY_PREF,orderNo);
            //发送消息回阻塞
            ThreadPoolTaskExecutor taskExecutor = Context.getBean("taskExecutor");
            taskExecutor.execute(() -> {
                DeviceSession session;
                for (int i = 1; i < 4; i++) {
                    session = DeviceSessionManager.getInstance().get(sn);
                    if (null != session) {
                        logger.debug("=======>获取到session,尝试发送第{}次报文", i);
                        Connection connection = session.getMConnection();
                        connection.sendResponse(resp);
                        RentOrder rentOrder = new RentOrder();
                        rentOrder.setCabinetSn(sn);
                        rentOrder.setOrderId(orderNo+"");
                        rentOrder.setSlotNum(Byte.toString(slotNum));
                        //等待08F5回复状态
                        rentOrder.setStatus("08F5");
                        try {
                            //把监听时间设置的长点
                            redisTemplate.opsForValue().set(cacheKey, objectMapper.writeValueAsString(rentOrder), 30,
                                    TimeUnit.SECONDS);
                        } catch (JsonProcessingException jpe) {
                            logger.debug("缓存失败！");
                        }
                        logger.debug("=======>第{}次发送报文成功!", i);
                        return;
                    } else {
                        try {
                            logger.debug("=======>未获取到session,尝试第{}次等待!", i);
                            //下发次数越多等待时间越久
                            Thread.sleep(i * 2000);
                        } catch (Exception e) {
                            logger.error("下发指令失败!");
                            logger.error(e.getLocalizedMessage(),e);
                        }
                    }
                }

                //租还没有反应，通知服务器
                messageService.send(sn,getBatterySn(sn,(int)slotNum),false,slotNum,orderNo,requestType,99,"");
            });


        } catch (Exception e) {
            logger.info(e.getLocalizedMessage());
            logger.error(e.getLocalizedMessage(),e);
        }
    }

    /**
     * 获取机柜缓存
     * @param cabinetSn
     * @return
     */
    public CabinetCacheVO getCabinetCache(String cabinetSn) {
        String cabinetInfo = (String) redisTemplate.opsForValue().get(cabinetSn);
        logger.info("从redis取机柜数据,cabinetSn:{},值:{}",cabinetSn,cabinetInfo);
        if (null != cabinetInfo && !"".equals(cabinetInfo)) {
            return JSONObject.parseObject(cabinetInfo, CabinetCacheVO.class);
        }
        return null;
    }

    /**
     * 获取电池SN
     * @param cabinetSn
     * @param slotNum
     * @return
     */
    public String getBatterySn(String cabinetSn,Integer slotNum) {
        CabinetCacheVO cabinetCacheVO = this.getCabinetCache(cabinetSn);
        if (null == cabinetCacheVO) {
            logger.error("机柜[{}]心跳信息不存在", cabinetSn);
        }
        List<CabinetCacheVO.SlotInfoBean> slotInfos = cabinetCacheVO.getSlot_info();
        for (CabinetCacheVO.SlotInfoBean slot: slotInfos) {
            if (slotNum == slot.getSlot_num()){
                return slot.getBattery_sn();
            }
        }
        return null;
    }
}
